#pragma once

//template class CSimpleMemPool
typedef void (*InitMemChunkFunc)(void* pChunk, void* pUserData);

template <class T>
class CSimpleMemPool
{
public:
	CSimpleMemPool(LPCTSTR lpszName = NULL);
	~CSimpleMemPool(void);

	void Initialize(unsigned int nChunkSize, unsigned int nChunkCount, InitMemChunkFunc pChunkInitFunc = NULL, void* pUserData = NULL);
	void Release();
	BOOL IsNull() const{
		return (m_pMemPool == NULL);
	}
	T* Allocate(unsigned int nBytes);
	void Free(void* pChunk);

	__int64 GetPoolAllocCount() const{
		return m_nPoolAllocCount;
	}

	__int64 GetSystemAllocCount() const{
		return m_nSystemAllocCount;
	}

	float GetPoolAllocPercent() const{
		if(m_nPoolAllocCount)
		{
			return (float)(m_nPoolAllocCount * 100.0/(m_nPoolAllocCount+m_nSystemAllocCount));
		}
		else
		{
			return 0.0f;
		}
	}

private:
	struct MEM_CHUNK;
	struct MEM_CHUNK_STUB
	{
		unsigned int poolID;
		MEM_CHUNK* pNextChunk;
	};

	struct MEM_CHUNK : public MEM_CHUNK_STUB
	{
		char chunkBuffer[1];
	};

	void* m_pMemPool;
	MEM_CHUNK* m_pFirstFreeChunk;
	unsigned int m_nChunkBufferSize;
	unsigned int m_nPoolSize;
	unsigned int m_nPoolID;
	__int64 m_nPoolAllocCount;
	__int64 m_nSystemAllocCount;
	TCHAR m_szName[64];
};


//template class pool_ptr_deleter
#define DEL_FUNC(t, pf)	void(*pf)(t *, void *)		

template<typename P>
class pool_ptr_deleter
{
public:
	explicit pool_ptr_deleter()
		:_pDelFunc(NoopDeleter)
		,_pUserData(0)
	{
	}

	explicit pool_ptr_deleter(CSimpleMemPool<P> &memPool)
		:_pDelFunc(MemPoolDeleter)
		,_pUserData(&memPool)
	{
	}

	explicit pool_ptr_deleter(DEL_FUNC(P, pDelFunc), void *pUserData)
		:_pDelFunc(pDelFunc)
		,_pUserData(pUserData)
	{
	}

	pool_ptr_deleter(const pool_ptr_deleter& _right)
		:_pDelFunc(_right._pDelFunc)
		,_pUserData(_right._pUserData)
	{
	}

	inline void operator()(P *Ptr)
	{
		_pDelFunc(Ptr, _pUserData);
	}

private:
	static void NoopDeleter(P *, void *)
	{
	}

	static void MemPoolDeleter(P *Ptr, void *pUserData)
	{
		reinterpret_cast<CSimpleMemPool<P> *>(pUserData)->Free(Ptr);
	}

	DEL_FUNC(P, _pDelFunc);
	void *_pUserData;
};

//template class pool_ptr

template<class P>
class pool_ptr
{
	typedef pool_ptr_deleter<P> Deleter;
public:
	explicit pool_ptr(P *Ptr = 0)
		:_MyPtr(Ptr)
	{
	}

	explicit pool_ptr(P *Ptr, CSimpleMemPool<P> &memPool)
		:_MyPtr(Ptr)
		,_Del(memPool)
	{
	}

	explicit pool_ptr(P *Ptr, Deleter d)
		:_MyPtr(Ptr)
		,_Del(d)
	{
	}

	pool_ptr(const pool_ptr<P> &_Right)
		:_MyPtr(_Right.release())
		,_Del(_Right._Del)
	{
	}

	pool_ptr<P>& operator=(const pool_ptr<P> &_Right)
	{
		reset(_Right.release());
		_Del = _Right._Del;
		return (*this);
	}

	~pool_ptr()
	{
		_Del(_MyPtr);
	}

	inline P& operator*() const
	{
		return (*_MyPtr);
	}

	inline P *operator->() const
	{
		return (&**this);
	}

	inline P *get() const
	{
		return (_MyPtr);
	}

	inline P *release() const
	{
		P *_Tmp = _MyPtr;
		const_cast<P *>(_MyPtr) = 0;
		return (_Tmp);
	}

	inline void reset(P* _Ptr = 0)
	{
		if (_Ptr != _MyPtr)
			_Del(_MyPtr);
		_MyPtr = _Ptr;
	}

private:
	P *_MyPtr;
	Deleter _Del;
};


//template class CSimpleMemPool implement
template<class T>
CSimpleMemPool<T>::CSimpleMemPool(LPCTSTR lpszName)
	: m_pMemPool(NULL)
	, m_pFirstFreeChunk(NULL)
	, m_nChunkBufferSize(0)
	, m_nPoolSize(0)
	, m_nPoolAllocCount(0)
	, m_nSystemAllocCount(0)
	, m_nPoolID(-1)
{
	memset(m_szName, 0, sizeof(m_szName));
	if(lpszName)
	{
		_tcsncpy(m_szName, lpszName, ARRAY_SIZE(m_szName)-1);
	}
}

template<class T>
CSimpleMemPool<T>::~CSimpleMemPool()
{
	Release();
}

template<class T>
void CSimpleMemPool<T>::Initialize(unsigned int nChunkSize, unsigned int nChunkCount, InitMemChunkFunc pChunkInitFunc /* = NULL */, void* pUserData /* = NULL */)
{
	ASSERT(m_pMemPool == NULL);
	if(m_pMemPool)
	{
		return;
	}

	m_nChunkBufferSize = _MAX(nChunkSize, sizeof(T));
	nChunkSize = m_nChunkBufferSize+sizeof(MEM_CHUNK_STUB);
	m_nPoolAllocCount = 0;
	m_nSystemAllocCount = 0;
	m_nPoolSize = nChunkSize * nChunkCount;
	m_pMemPool = malloc(m_nPoolSize);
	if(m_pMemPool == NULL)
	{
		m_pFirstFreeChunk = NULL;
		return;		
	}

	m_nPoolID = reinterpret_cast<unsigned int>(this);

	MEM_CHUNK* pPrevChunk = NULL;
	char* pMemPool = reinterpret_cast<char*>(m_pMemPool);
	for(unsigned int nChunkOffset = 0; nChunkOffset < m_nPoolSize; nChunkOffset+=nChunkSize)
	{
		MEM_CHUNK* pChunk = reinterpret_cast<MEM_CHUNK*>(pMemPool+nChunkOffset);
		if(pPrevChunk)
		{
			pPrevChunk->pNextChunk = pChunk;
		}

		pChunk->poolID = m_nPoolID;
		pChunk->pNextChunk = NULL;

		if(pChunkInitFunc)
		{
			pChunkInitFunc(pChunk->chunkBuffer, pUserData);
		}
		pPrevChunk = pChunk;
	}

	m_pFirstFreeChunk = reinterpret_cast<MEM_CHUNK*>(m_pMemPool);
}

template<class T>
void CSimpleMemPool<T>::Release()
{
	if(m_pMemPool)
	{
		free(m_pMemPool);
		m_pMemPool = NULL;
		m_pFirstFreeChunk = NULL;
		m_nChunkBufferSize = 0;
		m_nPoolID = -1;

		DEBUG_TRACE(_T("MemPool[%s] statistics:\n")
			  _T("             PoolSize = %d\n")
			  _T("             total pool alloc  :%I64d\n")
			  _T("             total system alloc:%I64d\n")
			  _T("             pool alloc percent:%4.2f%%\n"),
			  m_szName[0]?m_szName:_T("no name"),
			  m_nPoolSize,
			  GetPoolAllocCount(), GetSystemAllocCount(), GetPoolAllocPercent());
	}
}

template<class T>
T* CSimpleMemPool<T>::Allocate(unsigned int nBytes)
{
	if(nBytes > m_nChunkBufferSize || m_pFirstFreeChunk == NULL)
	{
		MEM_CHUNK* pChunk = reinterpret_cast<MEM_CHUNK *>(malloc(nBytes+sizeof(MEM_CHUNK_STUB)));
		if(pChunk)
		{
			pChunk->poolID = 0;
			pChunk->pNextChunk = NULL;
			T *pBuffer = reinterpret_cast<T*>(pChunk->chunkBuffer);
			++m_nSystemAllocCount;
			return pBuffer;
		}
		else
		{
			return NULL;
		}
	}
	else
	{
		T* pBuffer = reinterpret_cast<T*>(m_pFirstFreeChunk->chunkBuffer);
		m_pFirstFreeChunk = m_pFirstFreeChunk->pNextChunk;
		++m_nPoolAllocCount;
		return pBuffer;
	}
}

template<class T>
void CSimpleMemPool<T>::Free(void* pBuffer)
{
	if(pBuffer == 0)
	{
		return;
	}

	char* pChunkBuffer = reinterpret_cast<char *>(pBuffer);
	MEM_CHUNK* pMemChunk = reinterpret_cast<MEM_CHUNK*>(pChunkBuffer-sizeof(MEM_CHUNK_STUB));
	if(pMemChunk->poolID == m_nPoolID)
	{
		pMemChunk->pNextChunk = m_pFirstFreeChunk;
		m_pFirstFreeChunk = pMemChunk;
	}
	else if(pMemChunk->poolID == 0)
	{
		free(pMemChunk);
	}
}